// sparsefilemove.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "fileio.h"
#include "buffer.h"

void twofiles(int argc, wchar_t * argv[])
{
	if (argc != 5)
	{
		throw std::exception("need 4 filenames");
	}

	std::wstring source1(argv[1]);
	std::wstring target1(argv[2]);

	std::wstring source2(argv[3]);
	std::wstring target2(argv[4]);

	printf("%S sparsemove to %S\r\n", source1.c_str(), target1.c_str());
	printf("%S sparsemove to %S\r\n", source2.c_str(), target2.c_str());

	//
	// create target files
	//
	LARGE_INTEGER fileSize1;
	LARGE_INTEGER fileSize2;

	fileSize1 = FileIO::GetFileSize(source1);
	FileIO::CreateNewFileAsSparseWithSize(target1, fileSize1);

	fileSize2 = FileIO::GetFileSize(source2);
	FileIO::CreateNewFileAsSparseWithSize(target2, fileSize2);

	//
	// make source files sparse
	//
	FileIO::SetExistingFileSparse(source1);
	FileIO::SetExistingFileSparse(source2);

	Buffer buffer1;
	Buffer buffer2;

	bool fSource1Active = true;
	bool fSource2Active = true;
	bool fAnySourceActive = false;

	//
	// loop until no more source data remains
	//
	do
	{
		fAnySourceActive = false;

		if (fSource1Active && buffer1.Read(source1))
		{
			fAnySourceActive = true;
			buffer1.Write(target1);
			FileIO::MarkSparseRange(source1, buffer1.begin(), buffer1.size());
		}
		else
		{
			fSource1Active = false;
		}

		if (fSource2Active && buffer2.Read(source2))
		{
			fAnySourceActive = true;
			buffer2.Write(target2);
			FileIO::MarkSparseRange(source2, buffer2.begin(), buffer2.size());
		}
		else
		{
			fSource2Active = false;
		}
	} while (fAnySourceActive);

	// set the target files to not-sparse
	FileIO::SetExistingFileNotSparse(target1);
	FileIO::SetExistingFileNotSparse(target2);

	// delete now-empty source files
	FileIO::DeleteFile(source1);
	FileIO::DeleteFile(source2);
}

void onefile(int argc, wchar_t * argv[])
{
	if (argc != 3)
	{
		throw std::exception("need 2 filenames");
	}

	std::wstring fileSource(argv[1]);
	std::wstring fileTarget(argv[2]);
	printf("%S sparsemove to %S\r\n", fileSource.c_str(), fileTarget.c_str());

	//
	// get the files ready for operations
	//
	LARGE_INTEGER fileSize = FileIO::GetFileSize(fileSource);
	FileIO::CreateNewFileAsSparseWithSize(fileTarget, fileSize);
	FileIO::SetExistingFileSparse(fileSource);

	Buffer buffer;

	while (buffer.Read(fileSource))
	{
		buffer.Write(fileTarget);
		FileIO::MarkSparseRange(fileSource, buffer.begin(), buffer.size());
	}

	//
	// reset the file states
	//
	FileIO::SetExistingFileNotSparse(fileTarget);
	FileIO::DeleteFile(fileSource.c_str());
}

int wmain(int argc, wchar_t * argv[])
{
	for (int i = 0; i < argc; i++)
	{
		wprintf(argv[i]);
		wprintf(L"\r\n");
	}

	try
	{
		if (argc == 3)
		{
			onefile(argc, argv);
		}
		else if (argc == 5)
		{
			twofiles(argc, argv);
		}
		else
		{
			throw std::exception("need 2 or 4 filename arguments");
		}
		return 0;
	}
	catch (const std::exception & e)
	{
		printf(e.what());
		printf("\r\n");
	}
	catch (...)
	{
		printf("uncaught exception\r\n");
		throw;
	}

	return -1;
}
